#include <machine/asmdefs.h>
#include <machine/specialreg.h>

   .set noreorder
   
   .text
   .globl __start
   .type __start,@function
   .ent __start
__start:

   /*
    * Stack frame. We save the return address register, even though
    * it contains nothing useful. This is for gdb's benefit when it
    * comes disassembling. We also need 16 bytes for making a call,
    * so the total frame size is 20.
    *
    * Note that the frame here must match the frame we set up below
    * when we switch off the bootup stack. Otherwise, gdb gets very
    * confused.
    */
   .frame sp, 20, $0	/* 16-byte sp-relative frame; return addr on stack */
   .mask 0x80000000, -4	/* register 31 (ra) saved at (sp+20)-4 */
   addiu sp, sp, -20
   sw ra, 16(sp)
   
   /*
    * The System/161 loader sets up a boot stack for us at the top
    * of physical memory, and passes us a single string argument.
    * The string lives on the very top of the stack. We get its
    * address in a0.
    *
    * The kernel loads at virtual address 0x80001000, which is
    * physical address 0x00001000. The page immediately below this
    * is reserved for the exception vector code. 
    *
    * The symbol _end is generated by the linker. It's the address of
    * the end of the kernel. It's not a variable; the *value* of _end
    * itself is this address.
    *
    * We set up the memory map like this:
    *
    *         top of memory
    *                         free memory
    *         P + 0x1000
    *                         first thread's stack (1 page)
    *         P
    *                         wasted space (< 1 page)
    *                         copy of the boot string
    *         _end          
    *                         kernel
    *         0x80001000
    *                         exception handlers
    *         0x80000000
    *
    * where P is the next whole page after copying the argument string.
    */

   la s0, _end		/* stash _end in a saved register */
   
   move a1, a0		/* move bootstring to the second argument */
   move a0, s0		/* make _end the first argument */
   jal strcpy		/* call strcpy(_end, bootstring) */
   nop			/* delay slot */

   move a0, s0		/* make _end the first argument again */
   jal strlen		/* call strlen(_end) */
   nop

   add t0, s0, v0	/* add in the length of the string */
   addi t0, t0, 1	/* and the null terminator */
   
   
   addi t0, t0, 4095	/* round up to next page boundary */
   li   t1, 0xfffff000
   and  t0, t0, t1

   addi t0, t0, 4096	/* add one page to hold the stack */

   move sp, t0		/* start the kernel stack for the first thread here */
   sw t0, curkstack	/* which is also what we want our exceptions to use */

   sw t0, firstfree	/* remember the first free page for later */

   /*
    * At this point, s0 contains the boot argument string, and no other
    * registers contain anything interesting (except the stack pointer).
    */

   /*
    * Now set up a stack frame on the real kernel stack: a dummy saved
    * return address and four argument slots for making function calls.
    * (This needs to match the stack frame set up at the top of the
    * function, or the debugger gets confused.)
    */
   addiu sp, sp, -20
   sw $0, 16(sp)

   /*
    * Now, copy the exception handler code onto the first page of memory.
    */

   li a0, 0x80000000
   la a1, utlb_exception
   la a2, utlb_exception_end
   sub a2, a2, a1
   jal memmove
   nop

   li a0, 0x80000080
   la a1, exception
   la a2, exception_end
   sub a2, a2, a1
   jal memmove
   nop

   /*
    * Flush the instruction cache to make sure the above changes show
    * through to instruction fetch.
    */
   jal mips_flushicache
   nop

   /*
    * Initialize the TLB.
    */
   jal TLB_Reset
   nop

   /*
    * Set up the status register.
    *
    * The MIPS has six hardware interrupt lines and two software interrupts.
    * These are individually maskable in the status register. However, we
    * don't use this feature (for simplicity) - we only use the master 
    * interrupt enable/disable flag in bit 0. So enable all of those bits
    * now and forget about them.
    *
    * The BEV bit in the status register, if set, causes the processor to
    * jump to a different set of hardwired exception handling addresses.
    * This is so that the kernel's exception handling code can be loaded
    * into RAM and that the boot ROM's exception handling code can be ROM.
    * This flag is normally set at boot time, and we need to be sure to
    * clear it.
    *
    * The KUo/IEo/KUp/IEp/KUc/IEc bits should all start at zero.
    *
    * We also want all the other random control bits (mostly for cache
    * stuff) set to zero.
    *
    * Thus, the actual value we write is CST_IRQMASK.
    */
   
   li  t0, CST_IRQMASK		/* get value */
   mtc0 t0, c0_status		/* set status register */

   
   /*
    * We're all set up!
    * Fetch the copy of the bootstring as the argument, and call main.
    */
   jal kmain
   move a0, s0			/* in delay slot */


   /*
    * kmain shouldn't return. panic.
    * Loop back just in case panic returns.
    */
1:
   la  a0, panicstr
   jal panic
   nop				/* delay slot */
   j 1b
   nop				/* delay slot */
   .end __start

   .rdata
panicstr:
   .asciz "kmain returned\n"
